// --------------------------------------------------------------------------------------------------------------------
// <copyright file="OrderService.Front2Back.cs" company="Open Trader">
//   Copyright (c) David Denis (david.denis@systemathics.com)
// </copyright>
// <summary>
//   |  Open Trader - The Open Source Systematic Trading Platform
//   |
//   |  This program is free software: you can redistribute it and/or modify
//   |  it under the terms of the GNU General Public License as published by
//   |  the Free Software Foundation, either version 2 of the License, or
//   |  (at your option) any later version.
//   |
//   |  This program is distributed in the hope that it will be useful,
//   |  but WITHOUT ANY WARRANTY; without even the implied warranty of
//   |  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   |  GNU General Public License for more details.
//   |
//   |  You should have received a copy of the GNU General Public License
//   |  along with this program.  If not, see http://www.gnu.org/licenses
//   |
//   |  Up to date informations about Open Trader can be found at :
//   |    http://opentrader.org
//   |    http://opentrader.codeplex.com
//   |
//   |  For professional services, please visit us at :
//   |    http://www.systemathics.com
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Org.OpenTrader.Framework.Forge
{
    #region Usings

    using System;
    using System.Linq;
    using System.Threading;

    using Org.OpenTrader.Framework.Forge.DataModel;
    using Org.OpenTrader.Framework.Forge.Interfaces;

    #endregion

    /// <summary>
    /// The order service.
    /// </summary>
    public sealed partial class OrderService
    {
        /// <summary>
        /// The front 2 back.
        /// </summary>
        internal class Front2Back
        {
            #region Delegates

            /// <summary>
            /// The execute until success delegate.
            /// </summary>
            public delegate int ExecuteUntilSuccessDelegate();

            #endregion

            #region Public Methods

            /// <summary>
            /// The add order execution.
            /// </summary>
            /// <param name="quantity">
            /// The quantity.
            /// </param>
            /// <param name="price">
            /// The price.
            /// </param>
            /// <param name="counterparty">
            /// The counterparty.
            /// </param>
            /// <param name="order">
            /// The order.
            /// </param>
            public static void AddOrderExecution(int quantity, double price, string counterparty, IOrder order)
            {
                // Create the task
                var task = new AddOrderExecutionTask(quantity, price, Timings.Now, counterparty, order);

                // enqueue
                Singleton<QueueBackgroundWorker>.Instance.Enqueue(task);
            }

            /// <summary>
            /// The add order status.
            /// </summary>
            /// <param name="status">
            /// The status.
            /// </param>
            /// <param name="order">
            /// The order.
            /// </param>
            public static void AddOrderStatus(string status, IOrder order)
            {
                // Create the task
                var task = new AddOrderStatusTask(status, Timings.Now, order);

                // enqueue
                Singleton<QueueBackgroundWorker>.Instance.Enqueue(task);
            }

            #endregion

            /// <summary>
            /// The add dbOrder execution task.
            /// </summary>
            internal class AddOrderExecutionTask : Front2BackTask
            {
                #region Constants and Fields

                /// <summary>
                /// The counterparty.
                /// </summary>
                private string counterparty;

                /// <summary>
                /// The dbOrder.
                /// </summary>
                private IOrder order;

                /// <summary>
                /// The price.
                /// </summary>
                private double price;

                /// <summary>
                /// The quantity.
                /// </summary>
                private int quantity;

                /// <summary>
                /// The stamp.
                /// </summary>
                private DateTime stamp;

                #endregion

                #region Constructors and Destructors

                /// <summary>
                /// Initializes a new instance of the <see cref="AddOrderExecutionTask"/> class.
                /// </summary>
                /// <param name="quantity">
                /// The quantity.
                /// </param>
                /// <param name="price">
                /// The price.
                /// </param>
                /// <param name="stamp">
                /// The stamp.
                /// </param>
                /// <param name="counterparty">
                /// The counterparty.
                /// </param>
                /// <param name="order">
                /// The order.
                /// </param>
                public AddOrderExecutionTask(int quantity, double price, DateTime stamp, string counterparty, IOrder order)
                {
                    this.order = order;
                    this.quantity = quantity;
                    this.price = price;
                    this.stamp = stamp;
                    this.counterparty = counterparty;
                }

                #endregion

                #region Properties

                /// <summary>
                /// Gets Name.
                /// </summary>
                public override string Name
                {
                    get
                    {
                        return string.Format("AddOrderExecutionTask: Price={0} Quantity={1} OrderID={2}", this.price, this.quantity, this.order.ID);
                    }
                }

                #endregion

                #region Public Methods

                /// <summary>
                /// The run.
                /// </summary>
                public override void Run()
                {
                    var v0 = Timings.BenchmarkNow;

                    var r = this.ExecuteUntilSuccess(
                        () =>
                            {
                                // Prepare the execution
                                var dbExecution = DBOrderExecution.CreateDBOrderExecution(Guid.NewGuid(), this.quantity, this.price, this.stamp);
                                dbExecution.CounterParty = this.counterparty;

                                // Get related order and add execution to it
                                var dbOrder = this.GetOrCreateDBOrder(this.order);
                                dbOrder.DBOrderExecution.Add(dbExecution);

                                // Save changes
                                return Singleton<DBForge>.Instance.Commit();
                            });

                    Timings.Benchmark("AddOrderStatusTask", v0);
                }

                #endregion
            }

            /// <summary>
            /// The add order status task.
            /// </summary>
            internal class AddOrderStatusTask : Front2BackTask
            {
                #region Constants and Fields

                /// <summary>
                /// The db order.
                /// </summary>
                private IOrder order;

                /// <summary>
                /// The stamp.
                /// </summary>
                private DateTime stamp;

                /// <summary>
                /// The status.
                /// </summary>
                private string status;

                #endregion

                #region Constructors and Destructors

                /// <summary>
                /// Initializes a new instance of the <see cref="AddOrderStatusTask"/> class.
                /// </summary>
                /// <param name="status">
                /// The status.
                /// </param>
                /// <param name="stamp">
                /// The stamp.
                /// </param>
                /// <param name="order">
                /// The order.
                /// </param>
                public AddOrderStatusTask(string status, DateTime stamp, IOrder order)
                {
                    this.status = status;
                    this.stamp = stamp;
                    this.order = order;
                }

                #endregion

                #region Properties

                /// <summary>
                /// Gets Name.
                /// </summary>
                public override string Name
                {
                    get
                    {
                        return string.Format("AddOrderStatusTask: Status={0} OrderID={1}", this.status, this.order.ID);
                    }
                }

                #endregion

                #region Public Methods

                /// <summary>
                /// The run.
                /// </summary>
                public override void Run()
                {
                    var v0 = Timings.BenchmarkNow;

                    var r = this.ExecuteUntilSuccess(
                        () =>
                            {
                                // Prepare status
                                var dbOrderStatus = DBOrderStatus.CreateDBOrderStatus(Guid.NewGuid(), this.status, this.stamp);

                                // Get (or create) the related order
                                var dbOrder = this.GetOrCreateDBOrder(this.order);
                                dbOrder.DBOrderStatus.Add(dbOrderStatus);

                                // Save changes
                                return Singleton<DBForge>.Instance.Commit();
                            });

                    Timings.Benchmark("AddOrderStatusTask", v0);
                }

                #endregion
            }

            /// <summary>
            /// The front 2 back task.
            /// </summary>
            internal abstract class Front2BackTask : QueueBackgroundWorker.Task
            {
                #region Methods

                /// <summary>
                /// Loop forever until it works ! (entities really sucks !)
                /// </summary>
                /// <param name="d">
                /// The d.
                /// </param>
                /// <returns>
                /// The execute until success.
                /// </returns>
                protected int ExecuteUntilSuccess(ExecuteUntilSuccessDelegate d)
                {
                    var times = 0;
                    do
                    {
                        try
                        {
                            return d();
                        }
                        catch
                        {
                            times++;

                            // Stdout.WriteLine("** Failed (attempty={0}) time(s)", times);
                            Thread.Sleep(50);
                        }
                    }
                    while (true);
                }

                /// <summary>
                /// The get db order.
                /// </summary>
                /// <param name="order">
                /// The order.
                /// </param>
                /// <returns>
                /// </returns>
                protected DBOrder GetOrCreateDBOrder(IOrder order)
                {
                    var nodeId = Guid.Empty; // TODO split booking path is now nodified

                    DBOrder dbOrder;

                    // Get the related service
                    var dbServices = from s in Singleton<DBForge>.Instance.Entities.DBBackOffice where s.ID == order.OrderProviderID select s;
                    var dbService = dbServices.First();

                    // Get related order 
                    var dbOrders = from o in dbService.DBOrder where o.ID == order.ID select o;

                    // If non existing, add-it, else take the existing one
                    if (dbOrders.Count() == 0)
                    {
                        // Prepare order
                        dbOrder = DBOrder.CreateDBOrder(
                            order.ID, nodeId, order.InstrumentID, order.Quantity, order.Price, order.Way.ToString(), order.Type.ToString(), order.Stamp);

                        dbService.DBOrder.Add(dbOrder);
                    }
                    else
                    {
                        dbOrder = dbOrders.First();
                    }

                    return dbOrder;
                }

                #endregion
            }
        }
    }
}